package com.example.lambda

import org.junit.Test


/**
 * Lambda表达式
 *
 * @author zhouronghua
 * @time 2022/3/21 1:15 下午
 */
class LambdaTest {

    @Test
    fun testAnonymousFunction() {
        val len = "Joedan0104".count {
            it == 'd'
        }
        println("$len")
        // 匿名函数返回值类型推导(自动推导出返回值类型是String
        // 所以匿名函数的类型为：(v1:Int, v2:Int)->String)
        val method02 = { v1:Int, v2:Int ->
            "两个参数分别是[ $v1, $v2 ]"
        }
        println(method02(100, 200))
    }

    /**
     * 测试隐式返回
     *
     */
    @Test
    fun testFunction() {
        // 声明函数
        val methodAction: () -> String
        // 函数实现(匿名函数不要写return 最后一行就是返回值)
        methodAction = {
            // 返回值不能使用return，直接返回String对象。
            // 结果自动推断为最后一行，隐式返回
            "啊啊啊啊啊"
        }
        // 调用函数
        println("${methodAction()}")
    }

    /**
     * 测试函数声明和实现
     */
    @Test
    fun testFunDeclared() {
        // 函数声明 采用:号声明方法
        val method1: (Int, Int) -> Unit
        // 函数先声明，后实现
        println("==测试函数先声明后实现===")
        val method2: (Int) -> Unit
        method2 = {
            println("函数实现输入参数$it")
        }
        method2(25)
        println("==测试函数声明+实现===")
        // 函数声明+实现
        val method01 = { println("我是method01方法") }
        // 函数调用。Kotlin实现了操作符重载，()相当于invoke()方法
        method01()
        // 采用invoke()调用方法
        method01.invoke()
        // 函数声明+函数实现 = 号后面带函数实现体。
        // 参数value1的类型在声明中对应Int，参数value2在声明中对应Int
        // 函数体最后一个语句为返回值
        val method02: (Int, Int) -> String = { value1, value2 ->
            "这是两个参数的方法参数一：$value1 参数二：$value2"
        }
        println(method02(18, 38))
        // 测试函数参数拒接，拒接参数"_"标记
        println("==测试函数参数拒接===")
        val method03: (Int, Int) -> String = { _, value2 ->
            "这是两个参数的方法参数一：拒接 参数二：$value2"
        }
        println(method03(55, 66))
        // 直接在函数体定义参数类型
        println("==测试函数体中定义参数类型===")
        val method04 = { value1: Int, value2: Int ->
            "这是函数体两个参数的方法参数一：$value1 参数二：$value2"
        }
        println(method04(44, 44))
        // 只有一个参数可以直接使用it
        println("==测试函数只有一个参数默认采用it===")
        val method05: (Int) -> Unit = {
            println("使用it代替第一个参数$it")
        }
        method05(100)
        println("==测试函数体覆盖===")
        // 函数体覆盖override，后面的实现覆盖前面定义的函数体
        var method06 = { value1: Int, value2: Int ->
            "这是函数体两个参数的方法参数一：$value1 参数二：$value2"
        }
        // 重写函数体以后
        method06 = { value1: Int, value2: Int ->
            "重写函数体两个参数的方法参数一：$value1 参数二：$value2"
        }
        /**
         * Function2 method06 = (Function2)null.INSTANCE;
         * method06 = (Function2)null.INSTANCE;
         * Object var8 = method06.invoke(100, 101);
         */
        println(method06(100, 101))
        // 函数参数判断
        println("==测试函数参数when判断===")
        val method07 = { it: Int ->
            when (it) {
                1 -> println("你传递的是一")
                in 20..60 -> println("你传递的参数在20~60之间")
                else -> println("都不满足")
            }
        }
        method07.invoke(666)
        // 参数类型Any
        println("==测试函数参数Any===")
        val method08 = { str: Any -> str }
        println(method08(true))
        println(method08("中国"))
        // 函数体if表达式
        println("==测试函数体if表达式===")
        // == 等号是值的比较，===等号是对象的比较
        val method09 =
            { sex: Char -> if (sex == '男') "我是男生" else if (sex == '女') "我是女生" else "位置人类" }
        println(method09('女'))
        // 测试扩展函数
        println("==测试扩展函数==")
        "AAA".show()
        // 测试匿名扩展函数
        // String.() 给String类增加一个匿名扩展函数。我们Lambda表达式持有
        // String本身 == this
        // method19的Lambda体的函数返回值类型是Unit
        println("==测试匿名扩展函数==")
        var method19: String.() -> Unit = {
            println(this)
        }
        "DDD".method19()
        // 实现两数相加的结果
        // 匿名函数Int.(Int) -> String
        // 返回的结果是一个匿名函数 this代表第一个参数Int，Lambda体内持有扩展对象的引用
        // it对应扩展函数的输入参数(Int)，Lambda体内持有单个参数的引用it
        var method20: Int.(Int) -> String = { "两数相加的结果是 ${this + it}" }
        println(method20(10, 20))
        // 采用另外一种写法
        println(10.method20(20))
        println("==测试匿名扩展函数三个数相加==")
        // 扩展函数含有两个参数Double，返回值类型是Double，
        // 扩展函数需要声明返回值类型，不支持返回值类型智能推导
        var method21: Double.(Double, Double) -> Double = { value1, value2 ->
            this + value1 + value2
        }
        // Double扩展函数的使用
        println(98.20.method21(10.20, 20.20))
        // 函数调用的方式
        println(method21(98.20, 10.20, 20.20))
        // 第二阶段：测试函数输出
        println("==测试fun函数声明==")
        fun t01(): () -> Unit = {}  // 根据函数Lambda实现体，没有输入参数，没有输出参数-输出Unit

        // t02()是一个函数声明: ()->Double
        fun t02(): () -> Double = { 18.888 }
        // t02()怎么调用，函数体再加上()实现invoke()重载
        println(t02()())
        // 不增加函数声明，对应打印出函数的声明类型() -> kotlin.Double
        // 返回值类型不能在Lambda中采用return声明返回值类型
        fun t03() = { 20.000 }
        println(t03())
        // 函数体与run结合(函数体的实现)，实际就是一个函数的定义和实现
        fun t04(): Boolean = run {
            /*return */true
        }
        println(t04())
        // 函数t05()，对应的函数声明式：(Int)->Unit
        fun t05() = { n1: Int -> println("你输入的数据是$n1") }
        println(t05()(100))
        // fun aa() 与 var aa 有什么区别
        // t06()是一个实打实的函数
        fun t06() = {
            println("这是一个测试方法")
        }
        // t07接收一个匿名函数的变量，这个匿名函数，可以执行函数
        var t07 = {
            println("这是一个测试方法")
        }
    }

    /**
     * 定义String的扩展函数。
     * 扩展函数默认是public static的，全局可用
     *
     */
    fun String.show() {
        // 函数体默认带有this
        println("我是字符串$this")
    }

}